/*
*********************************************************************************************************
*                                                    LINUX-KERNEL
*                                        AllWinner Linux Platform Develop Kits
*                                                   Kernel Module
*
*                                    (c) Copyright 2006-2011, kevin.z China
*                                             All Rights Reserved
*
* File    : standby_twi.c
* By      : kevin.z
* Version : v1.0
* Date    : 2011-5-31 15:22
* Descript:
* Update  : date                auther      ver     notes
*********************************************************************************************************
*/
#include "standby_i.h"

#define TWI_CHECK_TIMEOUT       (0x2ff)

static __twic_reg_t*   TWI_REG_BASE[3] = {
    (__twic_reg_t*)IO_ADDRESS(AW_TWI0_BASE),
    (__twic_reg_t*)IO_ADDRESS(AW_TWI1_BASE),
    (__twic_reg_t*)IO_ADDRESS(AW_TWI2_BASE)
};

static __u32 TwiClkRegBak = 0;
static __u32 TwiCtlRegBak = 0;
static __twic_reg_t *twi_reg  = (__twic_reg_t *)(0);



/*
*********************************************************************************************************
*                                   standby_twi_init
*
*Description: init twi transfer.
*
*Arguments  :
*
*Return     :
*
*********************************************************************************************************
*/
__s32 standby_twi_init(int group)
{
	twi_reg  = TWI_REG_BASE[group];
	TwiClkRegBak = twi_reg->reg_clkr;
	TwiCtlRegBak = 0x80&twi_reg->reg_ctl;/* backup INT_EN;no need for BUS_EN(0xc0)  */
	//twi_reg->reg_clkr = (2<<3)|3; //100k
	twi_reg->reg_clkr = (5<<3)|0; //400k, M = 5, N=0;

	twi_reg->reg_reset |= 0x1;
	while(twi_reg->reg_reset&0x1);
	
	return 0;
}


/*
*********************************************************************************************************
*                                   standby_twi_exit
*
*Description: exit twi transfer.
*
*Arguments  :
*
*Return     :
*
*********************************************************************************************************
*/
__s32 standby_twi_exit(void)
{
    /* softreset twi module  */
    twi_reg->reg_reset |= 0x1;
    /* delay */
    //change_runtime_env();
    //delay_ms(10);

    /* restore clock division */
    twi_reg->reg_clkr = TwiClkRegBak;
    /* restore INT_EN */
    twi_reg->reg_ctl |= TwiCtlRegBak;
    return 0;
}


/*
*********************************************************************************************************
*                                   _standby_twi_stop
*
*Description: stop current twi transfer.
*
*Arguments  :
*
*Return     :
*
*********************************************************************************************************
*/
static int _standby_twi_stop(void)
{
    unsigned int   nop_read;
    unsigned int   timeout = TWI_CHECK_TIMEOUT;

    twi_reg->reg_ctl = (twi_reg->reg_ctl & 0xc0) | 0x10 | 0x08;/* set stop+clear int flag */

    nop_read = twi_reg->reg_ctl;
    nop_read = nop_read;
    // 1. stop bit is zero.
    while((twi_reg->reg_ctl & 0x10)&&(timeout--));
    if(timeout == 0)
    {
        return -1;
    }
    // 2. twi fsm is idle(0xf8).
    timeout = TWI_CHECK_TIMEOUT;
    while((0xf8 != twi_reg->reg_status)&&(timeout--));
    if(timeout == 0)
    {
        return -1;
    }
    // 3. twi scl & sda must high level.
    timeout = TWI_CHECK_TIMEOUT;
    while((0x3a != twi_reg->reg_lctl)&&(timeout--));
    if(timeout == 0)
    {
        return -1;
    }

    return 0;
}


/*
*********************************************************************************************************
*                                   twi_byte_rw
*
*Description: twi byte read and write.
*
*Arguments  : op        operation read or write;
*             saddr     slave address;
*             baddr     byte address;
*             data      pointer to the data to be read or write;
*
*Return     : result;
*               = EPDK_OK,      byte read or write successed;
*               = EPDK_FAIL,    btye read or write failed!
*********************************************************************************************************
*/
__s32 twi_byte_rw(enum twi_op_type_e op, __u8 saddr, __u8 baddr, __u8 *data)
{
    unsigned char state_tmp;
    unsigned int   timeout;
    int   ret = -1;

    twi_reg->reg_efr = 0;/* ��׼��д������0 */

    state_tmp = twi_reg->reg_status;
    if(state_tmp != 0xf8)
    {
        goto stop_out;
    }

    /* control registser bitmap
         7      6       5     4       3       2    1    0
      INT_EN  BUS_EN  START  STOP  INT_FLAG  ACK  NOT  NOT
    */

    //1.Send Start
    twi_reg->reg_ctl |= 0x20;
    timeout = TWI_CHECK_TIMEOUT;
    while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
    if(timeout == 0)
    {
        goto stop_out;
    }
    state_tmp = twi_reg->reg_status;
    if(state_tmp != 0x08)
    {
        goto stop_out;
    }

    //2.Send Slave Address
    twi_reg->reg_data = ((saddr<<1)&0xfe) | 0; /* slave address + write */
    twi_reg->reg_ctl &= 0xCF;
    timeout = TWI_CHECK_TIMEOUT;
    while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
    if(timeout == 0)
    {
        goto stop_out;
    }
    state_tmp = twi_reg->reg_status;
    if(state_tmp != 0x18)
    {
        goto stop_out;
    }

    //3.Send Byte Address
    twi_reg->reg_data = baddr;
    twi_reg->reg_ctl &= 0xCF;
    timeout = TWI_CHECK_TIMEOUT;
    while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
    if(timeout == 0)
    {
        goto stop_out;
    }
    state_tmp = twi_reg->reg_status;
    if(state_tmp != 0x28)
    {
        goto stop_out;
    }

    if(op == TWI_OP_WR)
    {
        //4.Send Data to be write
        twi_reg->reg_data = *data;
        twi_reg->reg_ctl &= 0xCF;
        timeout = TWI_CHECK_TIMEOUT;
        while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
        if(timeout == 0)
        {
            goto stop_out;
        }
        state_tmp = twi_reg->reg_status;
        if(state_tmp != 0x28)
        {
            goto stop_out;
        }
    }
    else
    {
        //4. Send restart for read
        twi_reg->reg_ctl = (twi_reg->reg_ctl & 0xc0) | 0x20 | 0x08;  /* set start+clear int flag */
        timeout = TWI_CHECK_TIMEOUT;
        while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
        if(timeout == 0)
        {
            goto stop_out;
        }
        state_tmp = twi_reg->reg_status;
        if(state_tmp != 0x10)
        {
            goto stop_out;
        }

        //5.Send Slave Address
        twi_reg->reg_data = (saddr<<1) | 1;/* slave address+ read */
	twi_reg->reg_ctl &= 0xCF;          /* clear int flag then 0x40 come in */
	timeout = TWI_CHECK_TIMEOUT;
        while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
        if(timeout == 0)
        {
            goto stop_out;
        }
        state_tmp = twi_reg->reg_status;
        if(state_tmp != 0x40)
        {
            goto stop_out;
        }

        //6.Get data
	twi_reg->reg_ctl &= 0xCF;          /* clear int flag then data come in */
        timeout = TWI_CHECK_TIMEOUT;
        while((!(twi_reg->reg_ctl & 0x08))&&(timeout--));
        if(timeout == 0)
        {
            goto stop_out;
        }
        *data = twi_reg->reg_data;
        state_tmp = twi_reg->reg_status;
        if(state_tmp != 0x58)
        {
          goto stop_out;
        }
    }

    ret = 0;

stop_out:
    //WRITE: step 5; READ: step 7
    //Send Stop
    _standby_twi_stop();

    return ret;
}

